
#pragma once

#include "ql.h"

/*
 * represents settings being applied to context allocators.
 * these values may be null - in this case when context initializes it will be
 * set to certain default values.
 * NB: shall not be changed after any allocation being performed
 */
typedef struct AllocContextSettings
{
    /*
     * this refers to the ids allocator
     */
    size_t                  id_arr_grow_size;
    size_t                  literals_arr_grow_size;

    /*
     * this refers to the lists allocator
     */
    size_t                  list_chunks_alloc_size;


    /*
     * this refers to the atoms allocator
     */
    size_t                  atom_chunks_grow_size;
} AllocContextSettings;

typedef struct AllocContext
{
    struct AllocContextImpl * impl;

    /*
     * tunes allocation process
     */
    AllocContextSettings    settings;

    /*
     * this is for internal use only
     */
    jmp_buf                 failure_jump;

    void                 (* failure_callback_p)( struct AllocContext * c, ErrResult failure );
}                   AllocContext;

void alloc_emit_error( AllocContext * c, ErrResult err );

/*
 * callback error function - can be set by user
 */
typedef void (* QL_ALLOC_GLOBAL_FAILURE_CALLBACK)( ErrResult failure );

void set_alloc_global_failure_callback( QL_ALLOC_GLOBAL_FAILURE_CALLBACK failure_callback );

ErrResult init_context( AllocContext * c );

void free_context( AllocContext * c );

// allocators
Id * alloc_id(
    AllocContext *  c,
    const char *    id,
    Id *            elem_namespace
    );

Id * alloc_id_len(
    AllocContext *  c,
    const char *    id,
    size_t          id_len,
    Id *            elem_namespace
    );

List * alloc_list( AllocContext * c );

/*
 * allocates list using variadic arguments list given.
 * last argument shall be null pointer, arguments right after arg1 shall be
 * pointers to Obj.
 */
List * alloc_listv( AllocContext * c, Obj * arg1, ... );

/*
 * allocates list using an array given
 */
List * alloc_listn( AllocContext * c, Obj ** obj_array, size_t obj_array_size );

IntNum * alloc_int_num( AllocContext * c, int val );

DblNum * alloc_dbl_num( AllocContext * c, double val );

Str * alloc_str( AllocContext * c, const char * str );

Str * alloc_str_len( AllocContext * c, const char * str, size_t len );

/*
 * allocates user-defined object
 * which size shall be the size of Obj struct + size for user defined data.
 */
Obj * alloc_user_defined_obj( AllocContext * c, size_t size );

/*
 * free one list
 */
void free_list( AllocContext * c, List * list );

/*
 * free list chain
 * helper function that allows to free list with its each node
 * last two arguments are optional - one is a pointer to callback function,
 * which will be applied for each list's node, last (tag) is a tag, provided to free_node_p function
 */
void free_list_chain(
    AllocContext *  c,
    List *          list,
    void (* free_node_p)(void *, Obj *),
    void *          tag
    );
